﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace SolutionClone.Model
{
    class SolutionCloner
    {
        // Step 0. Clone source project.
        // Step 1. Rename directories
        // Step 2. Rename the files
        // Step 3. Change file contents as needed

        public string SourceDirectory { get; set; }
        public string DestinationDirectory { get; set; }
        public string NewSolutionName { get; set; }
        public string OldSolutionName { get; set; }

        public void Clone()
        {
            CopyDirectory(this.SourceDirectory, this.DestinationDirectory);
            RenameDirectories(this.DestinationDirectory, this.OldSolutionName, this.NewSolutionName);
            RenameFiles(this.DestinationDirectory, this.OldSolutionName, this.NewSolutionName);
            ChangeFileContents(this.DestinationDirectory, this.OldSolutionName, this.NewSolutionName);
        }

        private void ChangeFileContents(string inputPath, string oldName, string newName)
        {
            var factory = new ConverterFactory();
            ForEachFile(inputPath,
                file =>
                {
                    var conv = factory.GetConverter(file, oldName, newName);
                    if (conv != null)
                        conv.Convert();
                });
        }
        private void RenameFiles(string inputPath, string oldName, string newName)
        {
            ForEachFile(inputPath,
                file =>
                {
                    var nFile = file.Replace(oldName, newName);
                    if (File.Exists(file) && (file != nFile))
                        File.Move(file, nFile);
                });

            ForEachSubDirectory(inputPath,
                s => RenameFiles(s, oldName, newName));
        }
        private void RenameDirectories(string inputPath, string oldName, string newName)
        {
            // Ensure that current directory is renamed if necessary.
            string newPath = inputPath.Replace(oldName, newName);
            if (Directory.Exists(inputPath) && (inputPath != newPath))
                Directory.Move(inputPath, newPath);

            // Grab child directories and call RenameDirectories recursively.
            ForEachSubDirectory(newPath, 
                s => RenameDirectories(s, oldName, newName));
        }

        private void ForEachFile(string inputPath, Action<string> action)
        {
            var files = Directory.GetFiles(inputPath);
            foreach (var lFile in files)
            {
                action.Invoke(lFile);
            }
        }
        private void ForEachSubDirectory(string inputPath, Action<string> action)
        {
            var subDirs = Directory.GetDirectories(inputPath);
            foreach (var sDir in subDirs)
            {
                action.Invoke(sDir);
            }
        }

        /// <summary>
        /// Copy a directory and all of its contents to 
        /// another path on the file system.
        /// </summary>
        /// <param name="srcDirectory">Directory that will be copied from.</param>
        /// <param name="dstDirectory">Directory that will be copied to.</param>
        private void CopyDirectory(string srcDirectory, string dstDirectory)
        {
            if (!Directory.Exists(dstDirectory))
                Directory.CreateDirectory(dstDirectory);

            foreach (string loopPath in Directory.GetFileSystemEntries(srcDirectory))
            {
                if (Directory.Exists(loopPath))
                    CopyDirectory(loopPath, Path.Combine(dstDirectory, Path.GetFileName(loopPath)));
                else
                    File.Copy(loopPath, Path.Combine(dstDirectory, Path.GetFileName(loopPath)), true);
            }
        }
    }
}
